package com.matrix.merchant.service.impl;

import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.matrix.api.lbs.dto.ShopInfoUpdateDTO;
import com.matrix.api.merchant.MerchantApi;
import com.matrix.api.merchant.dto.MerchantApplicationDTO;
import com.matrix.api.merchant.dto.MerchantReviewDTO;
import com.matrix.api.merchant.dto.ShopTableDTO;
import com.matrix.api.merchant.dto.ShopUpdateDTO;
import com.matrix.api.merchant.vo.MerchantApplicationVO;
import com.matrix.api.merchant.vo.ShopBriefInfoVO;
import com.matrix.api.merchant.vo.ShopTableVO;
import com.matrix.api.order.OrderApi;
import com.matrix.api.merchant.vo.ShopDetailVO;
import com.matrix.api.merchant.vo.ShopInfoVO;
import com.matrix.api.storage.StorageApi;
import com.matrix.api.storage.dto.FileUploadDTO;
import com.matrix.api.user.UserApi;
import com.matrix.api.user.vo.UserInfoVO;
import com.matrix.api.user.vo.UserLoginInfoVO;
import com.matrix.common.constant.RocketMQConstants;
import com.matrix.common.domain.PageResult;
import com.matrix.common.enums.ApplicationStatusEnum;
import com.matrix.common.enums.ShopStatusEnum;
import com.matrix.common.enums.ShopTableStatusEnum;
import com.matrix.common.enums.UserTypeEnum;
import com.matrix.common.exception.BusinessException;
import com.matrix.merchant.config.StorageProperties;
import com.matrix.merchant.converter.MerchantConverter;
import com.matrix.merchant.entity.MerchantApplication;
import com.matrix.merchant.entity.Shop;
import com.matrix.merchant.entity.ShopPhoto;
import com.matrix.merchant.entity.ShopTable;
import com.matrix.merchant.mapper.MerchantApplicationMapper;
import com.matrix.merchant.mapper.ShopMapper;
import com.matrix.merchant.mapper.ShopPhotoMapper;
import com.matrix.merchant.mapper.ShopTableMapper;
import com.matrix.merchant.service.MerchantService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.rpc.RpcException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.ZoneId;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 有点甜
 * @since 2025/7/26
 */
@Slf4j
@Service
@RequiredArgsConstructor
@DubboService
public class MerchantServiceImpl implements MerchantService, MerchantApi {

    private final MerchantApplicationMapper applicationMapper;
    private final ShopMapper shopMapper;
    private final ShopTableMapper shopTableMapper;
    private final ShopPhotoMapper shopPhotoMapper;
    private final RocketMQTemplate rocketMQTemplate;
    private final StorageProperties storageProperties;

    @DubboReference(check = false, timeout = 3000)
    private UserApi userApi;
    @DubboReference(check = false, timeout = 5000)
    private StorageApi storageApi;
    @DubboReference(check = false, timeout = 3000)
    private OrderApi orderApi;

    @Override
    public void applyForSettlement(Long userId, MerchantApplicationDTO applicationDTO) {
        LambdaQueryWrapper<MerchantApplication> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MerchantApplication::getUserId, userId);
        queryWrapper.in(MerchantApplication::getStatus, 0, 1);
        if (applicationMapper.selectCount(queryWrapper) > 0) {
            throw new BusinessException("您已有正在审核或已通过的入驻申请,请勿重复提交");
        }
        MerchantApplication application = MerchantConverter.INSTANCE.toMerchantApplication(applicationDTO);
        application.setUserId(userId);
        application.setStatus(ApplicationStatusEnum.PENDING);
        int inserted = applicationMapper.insert(application);
        if (inserted <= 0) {
            throw new BusinessException("提交申请失败,请稍后再试");
        }
    }

    @Override
    public PageResult<MerchantApplicationVO> getApplicationsByPage(int pageNum, int pageSize) {
        Page<MerchantApplication> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<MerchantApplication> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(MerchantApplication::getCreateTime);
        applicationMapper.selectPage(page, queryWrapper);
        List<MerchantApplicationVO> voList = MerchantConverter.INSTANCE.toMerchantApplicationVOList(page.getRecords());
        return new PageResult<>(voList, page.getTotal(), page.getPages());
    }

    @Override
    @Transactional
    public void batchUpdateMonthlySales(Map<Long, Integer> salesMap) {
        if (salesMap == null || salesMap.isEmpty()) {
            return;
        }

        for (Map.Entry<Long, Integer> entry : salesMap.entrySet()) {
            Shop shopUpdate = new Shop();
            shopUpdate.setId(entry.getKey());
            shopUpdate.setMonthlySales(entry.getValue());
            shopMapper.updateById(shopUpdate);
            Shop fullShop = shopMapper.selectById(entry.getKey());
            if (fullShop != null) {
                notifyLbsToUpdate(fullShop);
            }
        }
        log.info("成功批量更新 {} 个店铺的月销量。", salesMap.size());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reviewApplication(MerchantReviewDTO reviewDTO) {
        MerchantApplication application = applicationMapper.selectById(reviewDTO.getApplicationId());
        if (application == null || application.getStatus() != ApplicationStatusEnum.PENDING) {
            throw new BusinessException("申请不存在或状态不正确");
        }
        if (reviewDTO.getPassed()) {
            application.setStatus(ApplicationStatusEnum.APPROVED);
            Shop shop = new Shop();
            shop.setOwnerId(application.getUserId());
            shop.setShopName(application.getShopName());
            shop.setContactPhone(application.getContactPhone());
            shop.setAddressDetail(application.getAddressDetail());
            shop.setLocationLatitude(application.getLatitude());
            shop.setLocationLongitude(application.getLongitude());
            shop.setStatus(ShopStatusEnum.CLOSED);
            shop.setRating(new BigDecimal("5.0"));
            shop.setTasteRating(new BigDecimal("5.0"));
            shop.setPackagingRating(new BigDecimal("5.0"));
            shopMapper.insert(shop);
            notifyLbsToUpdate(shop);
        } else {
            if (!StringUtils.hasText(reviewDTO.getRejectionReason())) {
                throw new BusinessException("拒绝原因不能为空");
            }
            application.setStatus(ApplicationStatusEnum.REJECTED);
            application.setRejectionReason(reviewDTO.getRejectionReason());
        }
        applicationMapper.updateById(application);
    }

    @Override
    public ShopInfoVO getShopInfoByUserId(Long userId) {
        Shop shop = shopMapper.selectOne(new LambdaQueryWrapper<Shop>().eq(Shop::getOwnerId, userId));
        if (shop == null) {
            return null;
        }
        ShopInfoVO vo = new ShopInfoVO();
        vo.setShopId(shop.getId());
        vo.setShopName(shop.getShopName());
        return vo;
    }

    @Override
    @Transactional
    public void markTableForCleanUp(Long shopId, String areaSeat) {
        ShopTable table = findTableByAreaSeat(shopId, areaSeat);
        if (table != null && table.getStatus() == ShopTableStatusEnum.IN_USE) {
            ShopTable updateEntity = new ShopTable();
            updateEntity.setId(table.getId());
            updateEntity.setStatus(ShopTableStatusEnum.CLEANING_REQUIRED);
            updateEntity.setCurrentOrderId(null);
            shopTableMapper.updateById(updateEntity);
            log.info("桌位 {}-{} 已被标记为待清理", shopId, areaSeat);
        } else {
            String currentStatus = (table != null) ? table.getStatus().getDescription() : "不存在";
            log.warn("尝试将桌位 {}-{} 标记为待清理失败，当前状态为: {}", shopId, areaSeat, currentStatus);
        }
    }

    @Override
    public ShopDetailVO getShopDetailsById(Long shopId) {
        Shop shop = shopMapper.selectById(shopId);
        if (shop == null) {
            throw new BusinessException("店铺不存在");
        }
        ShopDetailVO vo = MerchantConverter.INSTANCE.toShopDetailVO(shop);
        List<ShopPhoto> photos = shopPhotoMapper.selectList(
                new LambdaQueryWrapper<ShopPhoto>().eq(ShopPhoto::getShopId, shopId).orderByAsc(ShopPhoto::getSortOrder));
        vo.setEnvironmentPhotos(photos.stream().map(ShopPhoto::getImageUrl).toList());
        try {
            Map<Long, Integer> salesMap = orderApi.getMonthlySalesByShopIds(Collections.singletonList(shopId));
            vo.setMonthlySales(salesMap.getOrDefault(shopId, 0));
        } catch (RpcException e) {
            log.error("获取店铺月销量失败, shopId: {}", shopId, e);
            vo.setMonthlySales(0);
        }
        return vo;
    }

    @Override
    public ShopDetailVO getShopDetailsInternal(Long shopId) {
        return getShopDetailsById(shopId);
    }

    @Override
    public ShopDetailVO getMyAndShopInfo(Long ownerId) {
        Shop shop = findShopByOwnerId(ownerId);
        return getShopDetailsById(shop.getId());
    }

    @Override
    @Transactional
    public void updateShopInfo(Long ownerId, ShopUpdateDTO shopUpdateDTO) {
        Shop shop = findShopByOwnerId(ownerId);
        MerchantConverter.INSTANCE.updateShopFromDTO(shopUpdateDTO, shop);

        if (shopUpdateDTO.getLocationLatitude() != null && shopUpdateDTO.getLocationLongitude() != null) {
            shop.setLocationLatitude(shopUpdateDTO.getLocationLatitude());
            shop.setLocationLongitude(shopUpdateDTO.getLocationLongitude());
        }
        shopMapper.updateById(shop);
        notifyLbsToUpdate(shop);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ShopTableVO createTable(Long ownerId, ShopTableDTO tableDTO) {
        Long shopId = findShopByOwnerId(ownerId).getId();
        if (shopTableMapper.selectCount(new LambdaQueryWrapper<ShopTable>()
                .eq(ShopTable::getShopId, shopId)
                .eq(ShopTable::getAreaName, tableDTO.getAreaName())
                .eq(ShopTable::getSeatNumber, tableDTO.getSeatNumber())) > 0) {
            throw new BusinessException("该区域已存在相同的座位号");
        }
        ShopTable table = MerchantConverter.INSTANCE.toShopTable(tableDTO);
        table.setShopId(shopId);
        table.setStatus(ShopTableStatusEnum.IDLE);
        shopTableMapper.insert(table);
        String qrCodeContent = String.format("matrix://shop/%d/table/%d", shopId, table.getId());
        String qrCodeUrl = generateAndUploadQrCode(qrCodeContent, ownerId);
        table.setQrCodeUrl(qrCodeUrl);
        shopTableMapper.updateById(table);
        return MerchantConverter.INSTANCE.toShopTableVO(table);
    }

    @Override
    @Transactional
    public ShopTableVO updateTable(Long ownerId, Long tableId, ShopTableDTO tableDTO) {
        Long shopId = findShopByOwnerId(ownerId).getId();
        ShopTable table = findTableAndCheckAuth(tableId, shopId);
        MerchantConverter.INSTANCE.updateShopTableFromDTO(tableDTO, table);
        shopTableMapper.updateById(table);
        return MerchantConverter.INSTANCE.toShopTableVO(table);
    }

    @Override
    @Transactional
    public void deleteTable(Long ownerId, Long tableId) {
        Long shopId = findShopByOwnerId(ownerId).getId();
        ShopTable table = findTableAndCheckAuth(tableId, shopId);
        if (table.getStatus() != ShopTableStatusEnum.IDLE) {
            throw new BusinessException("桌位正在使用中,无法删除");
        }
        shopTableMapper.deleteById(tableId);
    }

    @Override
    public List<ShopTableVO> listTables(Long ownerId) {
        Long shopId = findShopByOwnerId(ownerId).getId();
        return shopTableMapper.selectList(new LambdaQueryWrapper<ShopTable>().eq(ShopTable::getShopId, shopId))
                .stream().map(MerchantConverter.INSTANCE::toShopTableVO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void updateTableStatus(Long ownerId, Long tableId, Integer status) {
        Long shopId = findShopByOwnerId(ownerId).getId();
        ShopTable table = findTableAndCheckAuth(tableId, shopId);
        if (table.getStatus() == ShopTableStatusEnum.CLEANING_REQUIRED && status == ShopTableStatusEnum.IDLE.getCode()) {
            table.setStatus(ShopTableStatusEnum.IDLE);
            shopTableMapper.updateById(table);
        } else {
            throw new BusinessException("无效的状态变更操作");
        }
    }

    @Override
    public boolean checkTableAvailability(Long shopId, String areaSeat) {
        ShopTable table = findTableByAreaSeat(shopId, areaSeat);
        return table != null && table.getStatus() == ShopTableStatusEnum.IDLE;
    }

    @Override
    @Transactional
    public void occupyTable(Long shopId, String areaSeat, Long orderId) {
        ShopTable table = findTableByAreaSeat(shopId, areaSeat);
        if (table == null || table.getStatus() != ShopTableStatusEnum.IDLE) {
            throw new BusinessException("桌位不可用或不存在");
        }
        table.setStatus(ShopTableStatusEnum.IN_USE);
        table.setCurrentOrderId(orderId);
        shopTableMapper.updateById(table);
    }

    @Override
    @Transactional
    public void updateShopPhotos(Long ownerId, List<String> photoUrls) {
        Long shopId = findShopByOwnerId(ownerId).getId();
        if (photoUrls != null) {
            for (String url : photoUrls) {
                if (!url.startsWith(storageProperties.getResourceUrlPrefix())) {
                    throw new BusinessException("包含非法的图片URL,请重新上传！");
                }
            }
        }
        shopPhotoMapper.delete(new LambdaQueryWrapper<ShopPhoto>().eq(ShopPhoto::getShopId, shopId));
        if (photoUrls != null && !photoUrls.isEmpty()) {
            int sortOrder = 0;
            for (String url : photoUrls) {
                ShopPhoto photo = new ShopPhoto();
                photo.setShopId(shopId);
                photo.setImageUrl(url);
                photo.setSortOrder(sortOrder++);
                shopPhotoMapper.insert(photo);
            }
        }
    }

    @Override
    public PageResult<ShopBriefInfoVO> listMerchantsByPage(int pageNum, int pageSize, String shopName, String ownerPhone) {
        List<Long> ownerIds = null;
        if (StringUtils.hasText(ownerPhone)) {
            try {
                UserLoginInfoVO userResult = userApi.findByAccount(ownerPhone, UserTypeEnum.MERCHANT);
                if (userResult != null) {
                    ownerIds = List.of(userResult.getUserId());
                } else {
                    return new PageResult<>(Collections.emptyList(), 0, 0);
                }
            } catch (RpcException e) {
                log.error("调用用户服务查询商家手机号失败", e);
                return new PageResult<>(Collections.emptyList(), 0, 0);
            }
        }
        Page<Shop> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<Shop> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.hasText(shopName), Shop::getShopName, shopName);
        if (ownerIds != null) {
            queryWrapper.in(Shop::getOwnerId, ownerIds);
        }
        queryWrapper.orderByDesc(Shop::getCreateTime);
        shopMapper.selectPage(page, queryWrapper);
        List<Long> allOwnerIds = page.getRecords().stream().map(Shop::getOwnerId).distinct().toList();
        Map<Long, UserInfoVO> ownerMap = Collections.emptyMap();
        if (!allOwnerIds.isEmpty()) {
            try {
                ownerMap = userApi.getUsersByIds(allOwnerIds).stream().collect(Collectors.toMap(UserInfoVO::getUid, Function.identity()));
            } catch (RpcException e) {
                log.error("批量获取用户信息失败", e);
            }
        }
        List<ShopBriefInfoVO> voList = MerchantConverter.INSTANCE.toShopBriefInfoVOList(page.getRecords());
        Map<Long, UserInfoVO> finalOwnerMap = ownerMap;
        voList.forEach(vo -> {
            UserInfoVO owner = finalOwnerMap.get(vo.getOwnerId());
            if (owner != null) {
                vo.setOwnerPhone(owner.getPhone());
            }
        });

        return new PageResult<>(voList, page.getTotal(), page.getPages());
    }

    @Override
    @Transactional
    public void updateShopStatus(Long shopId, Integer statusValue) {
        ShopStatusEnum newStatus;
        if (statusValue == ShopStatusEnum.OPEN.getCode()) {
            newStatus = ShopStatusEnum.OPEN;
        } else if (statusValue == ShopStatusEnum.CLOSED.getCode()) {
            newStatus = ShopStatusEnum.CLOSED;
        } else {
            throw new BusinessException("无效的状态值");
        }

        Shop shop = shopMapper.selectById(shopId);
        if (shop == null) {
            throw new BusinessException("店铺不存在");
        }
        shop.setStatus(newStatus);
        shopMapper.updateById(shop);
        notifyLbsToUpdate(shop);
        log.info("店铺 {} 的状态已更新为: {}", shopId, newStatus.getDescription());
    }

    @Override
    @Transactional
    public void updateShopRatings(Long shopId, Map<String, BigDecimal> ratingsMap) {
        Shop shop = shopMapper.selectById(shopId);
        if (shop == null) {
            log.warn("尝试更新不存在的店铺评分，shopId: {}", shopId);
            return;
        }
        boolean needsUpdate = false;
        if (ratingsMap.containsKey("overall")) {
            shop.setRating(ratingsMap.get("overall"));
            needsUpdate = true;
        }
        if (ratingsMap.containsKey("taste")) {
            shop.setTasteRating(ratingsMap.get("taste"));
            needsUpdate = true;
        }
        if (ratingsMap.containsKey("packaging")) {
            shop.setPackagingRating(ratingsMap.get("packaging"));
            needsUpdate = true;
        }
        if (needsUpdate) {
            shopMapper.updateById(shop);
            notifyLbsToUpdate(shop);
            log.info("店铺 {} 的多维度评分已更新。", shopId);
        }
    }

    private void notifyLbsToUpdate(Shop shop) {
        if (shop == null) {
            return;
        }
        ShopInfoUpdateDTO dto = new ShopInfoUpdateDTO();
        dto.setShopId(shop.getId());
        dto.setShopName(shop.getShopName());
        dto.setLogoUrl(shop.getLogoUrl());
        dto.setRating(shop.getRating());
        dto.setStatus(shop.getStatus());
        dto.setMonthlySales(shop.getMonthlySales());
        dto.setCreateTime(Date.from(shop.getCreateTime().atZone(ZoneId.systemDefault()).toInstant()));
        if (shop.getLocationLatitude() != null && shop.getLocationLongitude() != null) {
            dto.setLatitude(shop.getLocationLatitude().doubleValue());
            dto.setLongitude(shop.getLocationLongitude().doubleValue());
        }
        rocketMQTemplate.convertAndSend(RocketMQConstants.SHOP_INFO_UPDATE_TOPIC, dto);
        log.info("已为店铺 {} 信息变更发送同步消息至LBS", shop.getId());
    }

    private String generateAndUploadQrCode(String content, Long userId) {
        BufferedImage image = QrCodeUtil.generate(content, new QrConfig(300, 300));
        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            ImageIO.write(image, "PNG", os);
            FileUploadDTO fileDTO = new FileUploadDTO(os.toByteArray(), "qrcode.png", "image/png");
            return storageApi.uploadInternalFile(fileDTO, userId, "qrcode");
        } catch (IOException e) {
            log.error("生成二维码图片失败", e);
            throw new BusinessException("二维码生成失败");
        } catch (RpcException e) {
            log.error("上传二维码到存储服务失败", e);
            throw new BusinessException("二维码上传失败，存储服务不可用");
        }
    }

    private ShopTable findTableByAreaSeat(Long shopId, String areaSeat) {
        if (!StringUtils.hasText(areaSeat) || !areaSeat.contains("-")) {
            log.warn("无效的 areaSeat 格式: {}", areaSeat);
            return null;
        }
        String[] parts = areaSeat.split("-", 2);
        String areaName = parts[0];
        String seatNumber = parts[1];
        return shopTableMapper.selectOne(new LambdaQueryWrapper<ShopTable>()
                .eq(ShopTable::getShopId, shopId)
                .eq(ShopTable::getAreaName, areaName)
                .eq(ShopTable::getSeatNumber, seatNumber));
    }

    private ShopTable findTableAndCheckAuth(Long tableId, Long shopId) {
        ShopTable table = shopTableMapper.selectById(tableId);
        if (table == null || !table.getShopId().equals(shopId)) {
            throw new BusinessException("桌位不存在或无权操作");
        }
        return table;
    }

    private Shop findShopByOwnerId(Long ownerId) {
        Shop shop = shopMapper.selectOne(new LambdaQueryWrapper<Shop>().eq(Shop::getOwnerId, ownerId));
        if (shop == null) {
            throw new BusinessException("店铺不存在或您无权操作");
        }
        return shop;
    }
}